<?php

namespace app\common;

use think\facade\Route;
use think\facade\Config;
use app\common\Attribute;

class Util
{
    /**
     * 小写转义 function
     *
     * @param string $string
     * @return string
     */
    public static function strislower(string $string): string
    {
        $firstChar = strtolower(substr($string, 0, 1));
        $restOfString = substr($string, 1);
        return $firstChar . $restOfString;
    }

    /**
     * 查看文件夹文件 function
     *
     * @param string $dir
     * @return array
     */
    public static function selectDir(string $dir): array
    {
        // 打开文件夹
        $files = [];
        if (is_dir($dir)) {
            if ($dh = opendir($dir)) {
                while (($filename = readdir($dh)) !== false) {
                    // 忽略当前目录和父目录
                    if ($filename != "." && $filename != "..") {
                        $files[] = $filename;
                    }
                }
                closedir($dh);
            }
        }
        return $files;
    }

    /**
     * 路由设置
     *
     * @param array $apps 应用名称 默认：["index"]
     * @param int $default 默认应用 默认：0
     * @return void
     */
    public static function route(array $apps = ["index"]): void
    {
        foreach ($apps as $app) {
            // 应用目录
            $controllerPath = app()->getAppPath() . $app . DIRECTORY_SEPARATOR . 'controller' . DIRECTORY_SEPARATOR;
            $controller_suffix = Config::get("route.controller_suffix") ? "Controller" : ""; // 控制器后缀
            // 控制器文件
            $controllers = glob($controllerPath . '*.php');
            foreach ($controllers as $files) {
                // 根据文件路径是被类名
                /** @var  $class_name *根据文件路径获取类名 */
                $class_name = "app\\" . str_replace('/', '\\', substr(substr($files, strlen(base_path())), 0, -4));
                if (!class_exists((string)$class_name)) {
                    // echo "Class $class_name not found, skip route for it\n";
                    continue;
                }
                // 控制器类
                $controller = new \ReflectionClass((string)$class_name);
                foreach ($controller->getMethods(\ReflectionMethod::IS_PUBLIC) as $v) {
                    /** 淘汰方法 */
                    if (in_array($v->name, ['__construct', '__destruct'])) {
                        continue;
                    }

                    $controller_str = $controller->getShortName(); //控制器名
                    $controller_name = str_replace($controller_suffix, "", Util::strislower($controller_str)); //控制器路径

                    $class = new \ReflectionMethod($v->class, $v->name);
                    $attributes = $class->getAttributes(Attribute::class);
                    $method_name = $v->name; //方法名

                    // 没有Attribute注解的方法，直接跳过
                    if (empty($attributes)) {
                        continue;
                    }

                    // 处理Attribute注解
                    foreach ($attributes as $attribute) {
                        // 处理Attribute注解
                        $data = $attribute->newInstance()->route();
                        $method = "*";
                        // 处理request注解
                        if (isset($data["request"]) && count($data["request"]) > 0) {
                            $method = implode("|", $data["request"]);
                        }
                        $method_str = $method_name;
                        // rule 路由规则
                        $rule = str_replace(
                            "//",
                            "/",
                            "{$app}/{$controller_name}/{$method_str}"
                        );
                        // 当字符串最后有"/"时，去掉
                        if (substr($rule, -1) == "/") {
                            $rule = substr($rule, 0, -1);
                        }
                        $name = "{$app}.{$controller_name}.{$method_str}";
                        $route = "\app\\{$app}\\controller\\{$controller_str}@{$method_name}";
                        self::addRoute(
                            $rule,
                            $route,
                            $method,
                            $data["middleware"] ?? [],
                            $name,
                            $app
                        );
                        self::indexRoute($rule, $route, $method, $data["middleware"] ?? [], $name, $app);
                    }
                }
            }
        }
    }

    /**
     * 添加路由
     *
     * @param string $rule
     * @param string $route
     * @param string $method
     * @param array $middleware
     * @param string $name
     * @param string $group
     * @return void
     */
    private static function addRoute(
        string $rule,
        string $route,
        string $method,
        array $middleware,
        string $name,
        string $group
    ): void {
        $route = Route::rule($rule, $route, $method);
        if (isset($middleware) && count($middleware) > 0) {
            $route->middleware($middleware);
        }
        $route->name($name);
        $route->group($group);
    }

    private static function indexRoute(
        string $rule,
        string $route,
        string $method,
        array $middleware,
        string $name,
        string $group
    ): void {
        // 判断rule最后是否"index",是则删除
        if (substr($rule, -5) == "index") {
            $rule = substr($rule, 0, -5);
        }
        $name = str_replace(".index", "", $name);
        if ($rule != "/" && substr($rule, -1) == "/") {
            $rule = substr($rule, 0, -1);
        } else if ($rule == "") {
            $rule = "/";
        } else {
            return;
        }
        self::addRoute($rule, $route, $method, $middleware, $name, $group);
        self::indexRoute($rule, $route, $method, $middleware, $name, $group);
    }

    /**
     * 名转class function
     *
     * @param string $class
     * @return string
     */
    public static function nameToClass(string $class): string
    {
        $class = preg_replace_callback(['/-([a-zA-Z])/', '/_([a-zA-Z])/'], function ($matches) {
            return strtoupper($matches[1]);
        }, $class);

        if (!($pos = strrpos($class, '/'))) {
            $class = ucfirst($class);
        } else {
            $path = substr($class, 0, $pos);
            $class = ucfirst(substr($class, $pos + 1));
            $class = "$path/$class";
        }
        return $class;
    }

    public static function classToName($class)
    {
        $class = lcfirst($class);
        return preg_replace_callback(['/([A-Z])/'], function ($matches) {
            return '_' . strtolower($matches[1]);
        }, $class);
    }

    public static function guessPath($base_path, $name, $return_full_path = false)
    {
        if (!is_dir($base_path)) {
            return false;
        }
        $names = explode('/', trim(strtolower($name), '/'));
        $realname = [];
        $path = $base_path;
        foreach ($names as $name) {
            $finded = false;
            foreach (scandir($path) ?: [] as $tmp_name) {
                if (strtolower($tmp_name) === $name && is_dir("$path/$tmp_name")) {
                    $path = "$path/$tmp_name";
                    $realname[] = $tmp_name;
                    $finded = true;
                    break;
                }
            }
            if (!$finded) {
                return false;
            }
        }
        $realname = implode(DIRECTORY_SEPARATOR, $realname);
        return $return_full_path ? self::get_realpath($base_path . DIRECTORY_SEPARATOR . $realname) : $realname;
    }

    public static function get_realpath(string $filePath): string
    {
        if (strpos($filePath, 'phar://') === 0) {
            return $filePath;
        } else {
            return realpath($filePath);
        }
    }
}
